/**============================================================================
 Confidential Information - Limited distribution to authorized persons only.
 This software is protected as an unpublished work under the U.S. Copyright
 Act of 1976.

 Copyright (c) 2011, BluePhoenix Solutions, Inc. All rights reserved.
 ----------------------------------------------------------------------------
 file......: DBHelper.java
 Author....: Vyacheslav Kovalyov
 Created...: 12/27/11

 Description:

 ============================================================================*/
package com.bphx.android.db;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.bphx.android.IStorage;
import com.bphx.android.exception.BaseException;
import com.bphx.android.words.Word;

import java.util.ArrayList;
import java.util.List;

import static com.bphx.android.db.DBConstants.*;
import static com.bphx.android.db.Tables.*;
import static com.bphx.android.exception.ExceptionFactory.getWordCreationException;
import static com.bphx.android.words.WordExceptionType.WORD_EXISTS;
import static java.lang.String.format;
import static org.apache.commons.lang.StringUtils.EMPTY;
import static org.apache.commons.lang.StringUtils.isBlank;

/**
 * @author Vyacheslav Kovalyov
 * @version 1.0
 */
public class Storage implements IStorage, IOptionsHolder {

    public Storage(Context context) {
        this.context = context;
        openHelper = new Database(context);
    }

    private void connect() {
        this.db = openHelper.getWritableDatabase();
    }

    @Override
    public ArrayList<Word> loadWords() {
        connect();

        List<DBWord> list = loadDBWords();
        ArrayList<Word> words = new ArrayList<Word>();

        for (DBWord dbWord : list) {
            words.add(createWord(dbWord));
        }

        closeDb();

        return words;
    }

    @Override
    public void removeWord(Word word) {
        removeWord(word, true);
    }

    public void removeWord(Word word, boolean needConnect) {
        if (needConnect) {
            connect();
        }
        db.execSQL(format(QUERY_DELTE_WORD_BY_NAME, word.getOriginalWord()));
        if (needConnect) {
            closeDb();
        }
    }

    @Override
    public void addWord(Word word) throws BaseException {

        connect();

        if (getWordId(word) < 0) {

            db.execSQL(format(QUERY_ADD_WORD, word.getOriginalWord()));
            addVariants(word);
            closeDb();
        } else {
            closeDb();
            throw getWordCreationException(WORD_EXISTS, context);
        }
    }

    private void addVariants(Word word) {
        int id = getWordId(word);

        addVariants(id, word.getVariants(), TRANS_VARIANTS);
        addVariants(id, word.getInvalidTranslations(), INVALID_TRANS_VARIANTS);
    }

    @Override
    public void replaceWord(Word word, Word originalWord) {

        if (word.equals(originalWord)) {
            return;
        }

        connect();
        int id = getWordId(originalWord);

        if (id >= 0) {
            removeWord(originalWord, false);
            db.execSQL(format(QUERY_ADD_WORD_WITH_ID, id, word.getOriginalWord()));
            addVariants(word);
        }

        closeDb();
    }

    @Override
    public String getOption(String option) {

        connect();

        String query = format(QUERY_GET_OPTION, option);
        Cursor cursor = db.rawQuery(query, null);

        String value = null;
        if (cursor.moveToFirst()) {
            value = cursor.getString(0);
        }

        closeCursor(cursor);
        closeDb();

        return value;
    }

    @Override
    public void saveOption(String option, String value) {

        connect();

        if (isBlank(option)) {
            throw new RuntimeException("Invalid option key");
        }
        if (value == null) {
            value = EMPTY;
        }

        db.execSQL(format(QUERY_SET_OPTION, option, value));

        closeDb();
    }

    private void addVariants(int wordId, List<String> variants, Tables table) {

        if (wordId < 0) {
            return;
        }

        String query = format(QUERY_SELECT_VARIANT, table.name(), wordId);
        Cursor cursor = db.rawQuery(query, null);

        List<String> dbVariants = new ArrayList<String>();
        if (cursor.moveToFirst()) {
            do {
                dbVariants.add(cursor.getString(0));
            } while (cursor.moveToNext());
        }

        for (String variant : variants) {
            addVariant(wordId, variant, dbVariants, table);
        }

        closeCursor(cursor);
    }

    private void addVariant(int wordId, String variant, List<String> variants, Tables table) {

        if (isBlank(variant)) {
            return;
        }

        for (String s : variants) {
            if (variant.equalsIgnoreCase(s)) {
                return;
            }
        }

        db.execSQL(format(QUERY_ADD_VARIANT, table.name(), wordId, variant));
    }

    private int getWordId(Word word) {

        int wordId = INCORRECT_ID;

        String query = format(QUERY_SELECT_WORD_ID_BY_NAME, word.getOriginalWord());
        Cursor cursor = db.rawQuery(query, null);

        if (cursor.moveToFirst()) {
            wordId = cursor.getInt(0);
        }

        closeCursor(cursor);
        return wordId;
    }

    private List<DBWord> loadDBWords() {

        String query = format(QUERY_BASE_SELECT, WORDS.name());
        Cursor cursor = db.rawQuery(query, null);

        List<DBWord> list = new ArrayList<DBWord>();

        if (cursor.moveToFirst()) {
            do {
                DBWord word = new DBWord(cursor.getInt(0), cursor.getString(1));
                list.add(word);
            } while (cursor.moveToNext());
        }

        closeCursor(cursor);

        return list;
    }

    private Word createWord(DBWord dbWord) {

        Word word = new Word(dbWord.getWord());

        word.setVariants(createVariants(dbWord, TRANS_VARIANTS));
        word.setInvalidTranslations(createVariants(dbWord, INVALID_TRANS_VARIANTS));

        return word;
    }

    private List<String> createVariants(DBWord dbWord, Tables table) {

        Cursor cursor = createSelectCursor(table, dbWord.getId());

        List<String> list = new ArrayList<String>();

        if (cursor.moveToFirst()) {
            do {
                list.add(cursor.getString(0));
            } while (cursor.moveToNext());
        }

        closeCursor(cursor);
        return list;
    }

    private void closeCursor(Cursor cursor) {

        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    private void closeDb() {

        if (db != null && db.isOpen()) {
            db.close();
        }
    }

    private Cursor createSelectCursor(Tables table, int id) {
        String query = format(QUERY_SELECT_BY_ID, table.name(), id);
        return db.rawQuery(query, null);
    }

    private SQLiteDatabase db;
    private final Database openHelper;
    private final Context context;
}
